package wangwenjun.phase3.atomic;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import util.ThreadUtil;

import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.stream.IntStream;

/**
 * @author ChangLiang
 * @date 2020/6/20
 */
@Slf4j
public class AtomicStampedReferenceTest {

    @Test
    @DisplayName("test cas")
    public void test() throws InterruptedException {
        ProductInventory productInventory = new ProductInventory(1, 5);
        AtomicStampedReference<ProductInventory> atomicStampedReference = new AtomicStampedReference<>(productInventory, 0);
        IntStream.range(0, 10).forEach((i) -> new Thread(() -> {
            try {
                reduceProductInventory(productInventory, atomicStampedReference);
            } catch (Exception e) {
                log.error(ThreadUtil.exceptionToString(e));
            }
        },
                "product-inventory-reduce-thread-" + i
        ).start());
        Thread.currentThread().join();
    }

    private void reduceProductInventory(ProductInventory productInventory, AtomicStampedReference<ProductInventory> atomicStampedReference) {
        boolean result = false;
        while(!result) {
            if (productInventory.getInventoryNum() <= 0) {
                throw new IllegalArgumentException("all the product has been sold out");
            }
            ProductInventory newProductInventory =
                    new ProductInventory(productInventory.getProductId(), productInventory.getInventoryNum() - 1);
            ThreadUtil.sleepForMilliSeconds(3_000);
            result = atomicStampedReference.compareAndSet(
                    productInventory,
                    newProductInventory,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp()+1);
            Integer tempNum = productInventory.getInventoryNum();
            if (!result) {
                productInventory = atomicStampedReference.getReference();
            }
            log.warn("reduce product inventory, result:{}  num from:{} to:{}",
                    result,
                    tempNum,
                    newProductInventory.getInventoryNum());
        }
    }
}
